package cn.hg.solon.youcan.framework.provider;

import org.dromara.hutool.core.convert.Convert;
import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.text.CharSequenceUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.http.useragent.UserAgent;
import org.dromara.hutool.http.useragent.UserAgentParser;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.event.EventBus;
import org.noear.solon.core.handle.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hg.solon.youcan.common.constant.AppConstants;
import cn.hg.solon.youcan.common.constant.CacheConstants;
import cn.hg.solon.youcan.common.enums.BeanStatus;
import cn.hg.solon.youcan.common.enums.BusinessStatus;
import cn.hg.solon.youcan.common.enums.OnlineStatus;
import cn.hg.solon.youcan.common.exception.ServiceException;
import cn.hg.solon.youcan.common.util.PasswordUtil;
import cn.hg.solon.youcan.framework.satoken.SaUtil;
import cn.hg.solon.youcan.framework.service.LoginService;
import cn.hg.solon.youcan.system.entity.SysDept;
import cn.hg.solon.youcan.system.entity.SysUser;
import cn.hg.solon.youcan.system.entity.SysUserLogin;
import cn.hg.solon.youcan.system.entity.SysUserOnline;
import cn.hg.solon.youcan.system.service.SysDeptService;
import cn.hg.solon.youcan.system.service.SysUserOnlineService;
import cn.hg.solon.youcan.system.service.SysUserService;

/**
 * @author 胡高
 */
@Component
public class LoginProvider implements LoginService {

    protected Logger log = LoggerFactory.getLogger(this.getClass());

    @Inject
    SysUserService userService;

    @Inject
    SysDeptService deptService;

    @Inject
    SysUserOnlineService userOnlineService;

    @Inject
    private CaptchaService captchaService;

    private void browserInfo(SysUserLogin login, Context ctx) {
        if (ObjUtil.isNull(ctx)) {
            return;
        }
        UserAgent agentInfo = UserAgentParser.parse(ctx.header("User-Agent"));

        login.setBrowser(agentInfo.getBrowser().getName());
        login.setOs(agentInfo.getOs().getName());
    }

    private void browserInfo(SysUserOnline online) {
        Context ctx = Context.current();
        if (ObjUtil.isNull(ctx)) {
            return;
        }
        UserAgent agentInfo = UserAgentParser.parse(ctx.header("User-Agent"));

        online.setBrowser(agentInfo.getBrowser().getName());
        online.setOs(agentInfo.getOs().getName());
    }

    @Override
    public SysUser currentUser() throws ServiceException {
        if (!StpUtil.isLogin()) {
            return null;
        }

        Integer id = StpUtil.getLoginIdAsInt();
        if (ObjUtil.isNull(id)) {
            return null;
        }

        return (SysUser)StpUtil.getTokenSession().get(CacheConstants.SYS_USER_TAG);
    }

    @Override
    public void login(String account, String password, String captcha) throws ServiceException {
        this.login(account, password, captcha, false);
    }

    @Override
    public void login(String account, String password, String captcha, boolean remember) throws ServiceException {
        CaptchaVO captchaVO = new CaptchaVO();
        captchaVO.setCaptchaVerification(captcha);

        ResponseModel response = this.captchaService.verification(captchaVO);

        if (!response.isSuccess()) {
            //登录日志
            this.loginLog(account, null, null, null, "验证码错误！");
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "验证码错误！");
        }

        SysUser user = this.userService.getByAccount(account);

        if (ObjUtil.isNull(user)) {
            //登录日志
            this.loginLog(account, user, null, BusinessStatus.FAILED, "账号或密码错误！");
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "账号或密码错误！");
        }

        if (CharSequenceUtil.equals(user.getStatus(), BeanStatus.OFF.name())) {
            //登录日志
            this.loginLog(account, user, null, BusinessStatus.FAILED, "账号已经被禁用！");
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "账号已经被禁用！");
        }

        if (!PasswordUtil.validatePassword(user.getSalt(), password, user.getPassword())) {
            //登录日志
            this.loginLog(account, user, null, BusinessStatus.FAILED, "账号或密码错误！");
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "账号或密码错误！");
        }

        if (StpUtil.isLogin()) {
            StpUtil.logout(StpUtil.getLoginId());
        }

        /*
         * SaToken登录
         */
        StpUtil.login(user.getId(), remember);

        SysDept dept = this.deptService.getById(user.getDeptId());

        SaUtil.setCurrentUser(user, dept);

        //登录日志
        this.loginLog(account, user, dept, BusinessStatus.SUCCESSED, "登录成功");

        /*
         * 在线处理
         */
        this.userOnline(user, dept);
    }

    private void loginLog(String account, SysUser user, SysDept dept, BusinessStatus status, String msg) {
        SysUserLogin login = new SysUserLogin();
        login.setAccount(account);
        Context ctx = Context.current();
        if (ObjUtil.isNotNull(ctx)) {
            login.setIp(ctx.realIp());
            login.setLoginDatetime(DateUtil.now());
            login.setMsg(msg);
            login.setStatus(status.name());
            this.browserInfo(login, ctx);
        }

        if (ObjUtil.isNotNull(user)) {
            login.setNickname(user.getNickname());
        }

        EventBus.pushAsync(login);

    }


    private void userOnline(SysUser user, SysDept dept) {
        /*
         * 创建Session
         */
        SaSession session = StpUtil.getTokenSession();
        SaTokenInfo token = StpUtil.getTokenInfo();

        SysUserOnline online = this.userOnlineService.getById(session.getId());

        if (ObjUtil.isNull(online)) {
            online = new SysUserOnline();
            online.setDeptName(dept.getName());
            online.setExpireSecond(Convert.toInt(session.getTimeout()));
            online.setId(token.getTokenValue());
            online.setDevice(StpUtil.getLoginDevice());
            online.setActivityDatetime(DateUtil.date(session.getCreateTime()));
            online.setNickname(user.getNickname());
            online.setStartDatetime(DateUtil.now());
            online.setStatus(OnlineStatus.ON.name());
            online.setUserId(ObjUtil.isNull(user) ? null : user.getId());
            this.browserInfo(online);

            try {
                this.userOnlineService.save(online);
            } catch (Exception e) {
                this.log.error("", e);
            }

            EventBus.pushAsync(online);
        } else {
            online.setExpireSecond(Convert.toInt(session.getTimeout()));
            online.setActivityDatetime(DateUtil.now());
            online.setStatus(OnlineStatus.ON.name());

            this.userOnlineService.updateById(online);
        }
    }

}
